package com.googlecode.ddlutilstool.kernel;

import java.awt.Color;
import java.io.IOException;

import javax.swing.JOptionPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

import org.apache.log4j.Logger;

import com.googlecode.ddlutilstool.common.GuiNameAccessor;
import com.googlecode.ddlutilstool.common.ProfileBean;
import com.googlecode.ddlutilstool.gui.DriverDialog;
import com.googlecode.ddlutilstool.gui.EditorDesktop;
import com.googlecode.ddlutilstool.gui.LogPanel;
import com.googlecode.ddlutilstool.gui.MainFrame;
import com.googlecode.ddlutilstool.gui.ProfileDialog;
import com.googlecode.ddlutilstool.gui.ProfilePanel;

/**
 * <p>Class GuiManager</p>
 * 
 * <p>This Singleton is a coordinater of the various Swing
 * components that make up the DDLUtilsTool.</p>
 * 
 * <p>It will create the frame, the menu, the panels and the
 * dialogs used by the DDLIUtilsTool.  It serves as the common
 * communication point between the components as well.</p>
 */
public class GuiManager {

	private static final Logger mLog = Logger.getLogger(GuiManager.class);
	private static GuiManager mInstance = null;
	
	public static GuiManager getInstance() {
		if (mInstance==null) {
			mInstance = new GuiManager();
		}
		return mInstance;
	}

	private GuiNameAccessor mGuiAccessor;
	private MainFrame mFrame;
	private EditorDesktop mEditorDesktop;
	private ProfilePanel mProfilePanel;
	private LogPanel mLogPanel;
	
	private GuiManager() {
	}
	
	/**
	 * Constructs the full GUI and displays it.
	 * 
	 * @throws ClassNotFoundException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws UnsupportedLookAndFeelException
	 */
	void init() throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException {
		mGuiAccessor = new GuiNameAccessor();
		
		//set the look and feel
		//UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
		UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		
		//change a few features of the UI
		Color background = UIManager.getColor("Panel.background");
		if (background!=null) {
			
			//set the menu backgrounds to match the panel background
			Color menuBarBackground = UIManager.getColor("MenuBar.background");
			if (menuBarBackground!=null && !background.equals(menuBarBackground)) {
				UIManager.put("MenuBar.background",background);
			}
			Color menuItemBackground = UIManager.getColor("MenuItem.background");
			if (menuItemBackground!=null && !background.equals(menuItemBackground)) {
				UIManager.put("MenuItem.background",background);
			}
			Color menuBackground = UIManager.getColor("Menu.background");
			if (menuItemBackground!=null && !background.equals(menuItemBackground)) {
				UIManager.put("Menu.background",menuBackground);
			}
		}
	
	
		mProfilePanel = new ProfilePanel();
		mEditorDesktop = new EditorDesktop();
		mLogPanel = new LogPanel();
		mFrame = new MainFrame(mProfilePanel,mEditorDesktop,mLogPanel);
		
		//display the frame
		mFrame.setVisible(true);
		mFrame.repaint();
	}
	
	/**
	 * Called to close the application, it checks for dirty editors,
	 * then calls System.exit(0).
	 */
	public void teardown() {
		if (mEditorDesktop.isAnyEditorDirty()) {
			int answer = question(mGuiAccessor.getString("editors.dirty.question"));
			if (answer==JOptionPane.YES_OPTION) {
				System.exit(0);	
			}
		}
		else {
			System.exit(0);
		}
	}

	/**
	 * Displays the question in a dialog.  The response
	 * is a constant in the JOptionPane class.
	 * 
	 * @param pMessage
	 * @return
	 */
	public int question(String pMessage) {
		return JOptionPane.showConfirmDialog(mFrame,
			pMessage,
			mGuiAccessor.getString("label.application"),
			JOptionPane.INFORMATION_MESSAGE);
	}

	/**
	 * Displays the error message in a dialog.
	 * 
	 * @param pErrorMessage
	 */
	public void error(String pErrorMessage) {
		JOptionPane.showMessageDialog(mFrame,
			pErrorMessage,
			mGuiAccessor.getString("label.application"),
			JOptionPane.ERROR_MESSAGE);

	}
	
	/**
	 * Dislays the information message in a dialog.
	 * 
	 * @param pErrorMessage
	 */
	public void info(String pErrorMessage) {
		JOptionPane.showMessageDialog(mFrame,
			pErrorMessage,
			mGuiAccessor.getString("label.application"),
			JOptionPane.INFORMATION_MESSAGE);

	}

	/**
	 * Called by the menu option "Edit Profile", it displays
	 * the Profile Editor with the currently selected profile.
	 */
	public void editProfile() {
		ProfileBean profile = mProfilePanel.getProfile();
		if (profile==null) {
			error(mGuiAccessor.getString("profile.dialog.none.selected"));
		}
		else {
			editProfile(profile.getName());
		}
	}
	
	/**
	 * Called by either the menu option "Edit Profile" or double-clicking
	 * a profile in the profile list, it will open the profile dialog
	 * and allow the user to change the profile.
	 * 
	 * @param pProfileName
	 */
	public void editProfile(String pProfileName) {
		if (mLog.isDebugEnabled()) mLog.debug("Editing profile "+pProfileName);
		ProfileDialog dialog = new ProfileDialog(mFrame);
		ProfileBean profile = dialog.display(ProfileManager.getInstance().getProfile(pProfileName));
		if (profile!=null) {
			mProfilePanel.fillList();
			mProfilePanel.setProfile(profile);
		}
	}

	/**
	 * Called by the Add Profile menu option, it will dislay the profile
	 * editor with a blank profile.
	 */
	public void addProfile() {
		if (mLog.isDebugEnabled()) mLog.debug("Adding new profile");
		ProfileDialog dialog = new ProfileDialog(mFrame);
		ProfileBean profile = dialog.display();
		if (profile!=null) {
			mProfilePanel.fillList();
			mProfilePanel.setProfile(profile);
		}
	}
	
	/**
	 * Called by the Delete Profile menu option, it asks
	 * for a confirmation, then deletes the profile using
	 * the ProfileManager.
	 */
	public void deleteProfile() {
		if (mLog.isDebugEnabled()) mLog.debug("Delete profile.");
		ProfileBean profile = mProfilePanel.getProfile();
		if (profile==null) {
			error(mGuiAccessor.getString("profile.dialog.none.selected"));
		}
		else {
			String dialog = profile.getName();
			int answer = question(mGuiAccessor.getString("profile.dialog.delete.question",dialog));
			if (answer==JOptionPane.YES_OPTION) {
				try {
					ProfileManager.getInstance().removeProfile(dialog);
				}
				catch (IOException e) {
					error(mGuiAccessor.getString("profile.dialog.profile.delete.error",e.getMessage()));
					return;
				}
				mProfilePanel.setProfile(null);
				mProfilePanel.fillList();
			}
			
		}
	}
	
	/**
	 * Called by the Edit Drivers menu option, it displays the Driver
	 * dialog.
	 */
	public void editDrivers() {
		if (mLog.isDebugEnabled()) mLog.debug("Editing Drivers...");
		DriverDialog dialog = new DriverDialog(mFrame);
		dialog.display();
	}
	
	/**
	 * Writes the contents of the editor window as a schema DDL to
	 * the profile database.
	 * 
	 */
	public void startSchemaDDLToDatabase() {
		if (mLog.isDebugEnabled()) mLog.debug("Execute Schema DDL to Database.");
		
		ProfileBean profile = mProfilePanel.getProfile();
		if (profile==null) {
			error(mGuiAccessor.getString("execute.no.profile.selected"));
			return;
		}
		if (mEditorDesktop.isCurrentEditorDirty()) {
			int answer = question(mGuiAccessor.getString("execute.dirty.editor"));
			if (answer==JOptionPane.YES_OPTION) {
				if (!mEditorDesktop.fileSave()) {
					return;
				}
			}
			else if (answer==JOptionPane.CANCEL_OPTION) {
				return;
			}
		}
		if (mEditorDesktop.isCurrentEditorEmpty()) {
			error(mGuiAccessor.getString("execute.no.file"));
			return;
		}
		
		ExecuteManager exe = ExecuteManager.getInstance();
		
		if (exe.isBusy()) {
			error(mGuiAccessor.getString("execute.busy"));
			return;
		}
		
		mLogPanel.clean();
		mFrame.disableForExecution();
		
		try {
			exe.executeSchemaDDLToDatabase(new ExecuteCompleteListener() {
				public void finishedExecution(boolean pSuccess, String pMessage, String pContent) {
					if (pSuccess) {
						info(mGuiAccessor.getString("execute.success.ddl.to.database"));
					}
					else {
						error(mGuiAccessor.getString("execute.error.ddl.to.database",pMessage));
					}
					mFrame.reenableAfterExecution();
				}
			},profile, mEditorDesktop.getCurrentEditorReader());
		}
		catch (Exception e) {
			error(mGuiAccessor.getString("execute.busy"));
			return;
		}
	}
	
	/**
	 * Using the current profile, gets the database schema, creates the
	 * DDL and writes it to the editor.
	 */
	public void startSchemaDDLToFile() {
		if (mLog.isDebugEnabled()) mLog.debug("Execute Schema DDL to File.");
		
		ProfileBean profile = mProfilePanel.getProfile();
		if (profile==null) {
			error(mGuiAccessor.getString("execute.no.profile.selected"));
			return;
		}
		if (mEditorDesktop.isCurrentEditorDirty()) {
			int answer = question(mGuiAccessor.getString("execute.dirty.editor"));
			if (answer==JOptionPane.YES_OPTION) {
				if (!mEditorDesktop.fileSave()) {
					return;
				}
			}
			else if (answer==JOptionPane.CANCEL_OPTION) {
				return;
			}
		}
		
		ExecuteManager exe = ExecuteManager.getInstance();
		
		if (exe.isBusy()) {
			error(mGuiAccessor.getString("execute.busy"));
			return;
		}
		
		mLogPanel.clean();
		mFrame.disableForExecution();
		
		try {
			exe.executeSchemaDDLToFile(new ExecuteCompleteListener() {
				public void finishedExecution(boolean pSuccess, String pMessage, String pContent) {
					if (pSuccess) {
						mEditorDesktop.fileNew(pContent,"Untitled.xml");
						info(mGuiAccessor.getString("execute.success.ddl.to.file"));
					}
					else {
						error(mGuiAccessor.getString("execute.error.ddl.to.file",pMessage));
					}
					mFrame.reenableAfterExecution();
				}
			},profile);
		}
		catch (Exception e) {
			error(mGuiAccessor.getString("execute.busy"));
			return;
		}
	}

	/**
	 * <p>Compares the contents of the DDL XML, and the schema of the
	 * database, and create SQL to alter the database to match the
	 * DDL XML.  It places the SQL into an editor window.</p>
	 * 
	 * <p>Before starting, it checks that a valid profile is selected, 
	 * there is a selected editor with content, and that the manager is
	 * not busy.  It will ask if the user wants to save the current
	 * editor if it is busy.</p> 
	 * 
	 */
	public void startAlterSchemaDDLToSQL() {
		if (mLog.isDebugEnabled()) mLog.debug("Execute Schema DDL to SQL.");
		
		ProfileBean profile = mProfilePanel.getProfile();
		if (profile==null) {
			error(mGuiAccessor.getString("execute.no.profile.selected"));
			return;
		}
		if (mEditorDesktop.isCurrentEditorDirty()) {
			int answer = question(mGuiAccessor.getString("execute.dirty.editor"));
			if (answer==JOptionPane.YES_OPTION) {
				if (!mEditorDesktop.fileSave()) {
					return;
				}
			}
			else if (answer==JOptionPane.CANCEL_OPTION) {
				return;
			}
		}
		if (mEditorDesktop.isCurrentEditorEmpty()) {
			error(mGuiAccessor.getString("execute.no.file"));
			return;
		}
		
		ExecuteManager exe = ExecuteManager.getInstance();
		
		if (exe.isBusy()) {
			error(mGuiAccessor.getString("execute.busy"));
			return;
		}
		
		mLogPanel.clean();
		mFrame.disableForExecution();
		
		try {
			exe.executeAlterSchemaDDLToSQL(new ExecuteCompleteListener() {
				public void finishedExecution(boolean pSuccess, String pMessage, String pContent) {
					if (pSuccess) {
						mEditorDesktop.fileNew(pContent,"Untitled.sql");
						info(mGuiAccessor.getString("execute.success.ddl.to.sql"));
					}
					else {
						error(mGuiAccessor.getString("execute.error.ddl.to.sql",pMessage));
					}
					mFrame.reenableAfterExecution();
				}
			},	profile, mEditorDesktop.getCurrentEditorReader());
		}
		catch (Exception e) {
			error(mGuiAccessor.getString("execute.busy"));
			return;
		}
	}

	
	/**
	 * <p>Create SQL to create a a database schema.</p>
	 * 
	 * <p>Before starting, it checks that a valid profile is selected, 
	 * and that the manager is not busy.</p> 
	 * 
	 */
	public void startCreateSchemaDDLToSQL() {
		if (mLog.isDebugEnabled()) mLog.debug("Execute Schema DDL to SQL.");
		
		ProfileBean profile = mProfilePanel.getProfile();
		if (profile==null) {
			error(mGuiAccessor.getString("execute.no.profile.selected"));
			return;
		}
		ExecuteManager exe = ExecuteManager.getInstance();
		
		if (exe.isBusy()) {
			error(mGuiAccessor.getString("execute.busy"));
			return;
		}
		
		mLogPanel.clean();
		mFrame.disableForExecution();
		
		try {
			exe.executeCreateSchemaDDLToSQL(new ExecuteCompleteListener() {
				public void finishedExecution(boolean pSuccess, String pMessage, String pContent) {
					if (pSuccess) {
						mEditorDesktop.fileNew(pContent,"Untitled.sql");
						info(mGuiAccessor.getString("execute.success.ddl.to.sql"));
					}
					else {
						error(mGuiAccessor.getString("execute.error.ddl.to.sql",pMessage));
					}
					mFrame.reenableAfterExecution();
				}
			},	profile);
		}
		catch (Exception e) {
			error(mGuiAccessor.getString("execute.busy"));
			return;
		}
	}
	
	/**
	 * <p>Writes the contents of the editor window as a schema DDL to
	 * the profile database.</p>
	 * 
	 * <p>Before starting, it checks that a valid profile is selected, 
	 * there is a selected editor with content, and that the manager is
	 * not busy.  It will ask if the user wants to save the current
	 * editor if it is busy.</p> 
	 */
	public void startDataDDLToDatabase() {
		if (mLog.isDebugEnabled()) mLog.debug("Execute Data DDL to Database.");
		
		ProfileBean profile = mProfilePanel.getProfile();
		if (profile==null) {
			error(mGuiAccessor.getString("execute.no.profile.selected"));
			return;
		}
		if (mEditorDesktop.isCurrentEditorDirty()) {
			int answer = question(mGuiAccessor.getString("execute.dirty.editor"));
			if (answer==JOptionPane.YES_OPTION) {
				if (!mEditorDesktop.fileSave()) {
					return;
				}
			}
			else if (answer==JOptionPane.CANCEL_OPTION) {
				return;
			}
		}
		if (mEditorDesktop.isCurrentEditorEmpty()) {
			error(mGuiAccessor.getString("execute.no.file"));
			return;
		}
		
		ExecuteManager exe = ExecuteManager.getInstance();
		
		if (exe.isBusy()) {
			error(mGuiAccessor.getString("execute.busy"));
			return;
		}
		
		mLogPanel.clean();
		mFrame.disableForExecution();
		
		try {
			exe.executeDataDDLToDatabase(	new ExecuteCompleteListener() {
				public void finishedExecution(boolean pSuccess, String pMessage, String pContent) {
					if (pSuccess) {
						info(mGuiAccessor.getString("execute.success.ddldata.to.database"));
					}
					else {
						error(mGuiAccessor.getString("execute.error.ddldata.to.database",pMessage));
					}
					mFrame.reenableAfterExecution();
				}
			},	profile, mEditorDesktop.getCurrentEditorReader());
		}
		catch (Exception e) {
			error(mGuiAccessor.getString("execute.busy"));
			return;
		}
	}

	/**
	 * <p>Using the current profile, gets the database schema, creates the
	 * DDL and writes it to the editor.</p>
	 * 
	 * <p>Before starting, it checks that a valid profile is selected, 
	 * there is a selected editor with content, and that the manager is
	 * not busy.  It will ask if the user wants to save the current
	 * editor if it is busy.</p> 
	 */
	public void startDataDDLToFile() {
		if (mLog.isDebugEnabled()) mLog.debug("Execute Data DDL to File.");
		
		ProfileBean profile = mProfilePanel.getProfile();
		if (profile==null) {
			error(mGuiAccessor.getString("execute.no.profile.selected"));
			return;
		}
		if (mEditorDesktop.isCurrentEditorDirty()) {
			int answer = question(mGuiAccessor.getString("execute.dirty.editor"));
			if (answer==JOptionPane.YES_OPTION) {
				if (!mEditorDesktop.fileSave()) {
					return;
				}
			}
			else if (answer==JOptionPane.CANCEL_OPTION) {
				return;
			}
		}
		
		ExecuteManager exe = ExecuteManager.getInstance();
		
		if (exe.isBusy()) {
			error(mGuiAccessor.getString("execute.busy"));
			return;
		}
		
		mLogPanel.clean();
		mFrame.disableForExecution();
		
		try {
			exe.executeDataDDLToFile(new ExecuteCompleteListener() {
				public void finishedExecution(boolean pSuccess, String pMessage, String pContent) {
					if (pSuccess) {
						mEditorDesktop.fileNew(pContent,"Untitled.xml");
						info(mGuiAccessor.getString("execute.success.dataddl.to.file"));
					}
					else {
						error(mGuiAccessor.getString("execute.error.dataddl.to.file",pMessage));
					}
					mFrame.reenableAfterExecution();
				}
			},	profile);
		}
		catch (Exception e) {
			error(mGuiAccessor.getString("execute.busy"));
			return;
		}
	}

	
	/**
	 * <p>Using the current profile, gets the database schema, creates the
	 * SQL and writes it to the editor.</p>
	 * 
	 * <p>Before starting, it checks that a valid profile is selected, 
	 * there is a selected editor with content, and that the manager is
	 * not busy.  It will ask if the user wants to save the current
	 * editor if it is busy.</p> 
	 */
	public void startDataDDLToSQL() {
		if (mLog.isDebugEnabled()) mLog.debug("Execute Data DDL to File.");
		
		ProfileBean profile = mProfilePanel.getProfile();
		if (profile==null) {
			error(mGuiAccessor.getString("execute.no.profile.selected"));
			return;
		}
		if (mEditorDesktop.isCurrentEditorDirty()) {
			int answer = question(mGuiAccessor.getString("execute.dirty.editor"));
			if (answer==JOptionPane.YES_OPTION) {
				if (!mEditorDesktop.fileSave()) {
					return;
				}
			}
			else if (answer==JOptionPane.CANCEL_OPTION) {
				return;
			}
		}
		
		ExecuteManager exe = ExecuteManager.getInstance();
		
		if (exe.isBusy()) {
			error(mGuiAccessor.getString("execute.busy"));
			return;
		}
		
		mLogPanel.clean();
		mFrame.disableForExecution();
		
		try {
			exe.executeDataDDLToSQL(new ExecuteCompleteListener() {
				public void finishedExecution(boolean pSuccess, String pMessage, String pContent) {
					if (pSuccess) {
						mEditorDesktop.fileNew(pContent,"Untitled.xml");
						info(mGuiAccessor.getString("execute.success.dataddl.to.sql"));
					}
					else {
						error(mGuiAccessor.getString("execute.error.dataddl.to.sql",pMessage));
					}
					mFrame.reenableAfterExecution();
				}
			},profile);
		}
		catch (Exception e) {
			error(mGuiAccessor.getString("execute.busy"));
			return;
		}
	}

}
